/************************************************************************
                                                                     
   Xceed Ultimate ListBox for Silverlight                                                                                                                                            
   Copyright (C) 2010 Xceed Software Inc.    
                                                                     
   This program is provided to you under the terms of the GNU General Public  
   License version 2 as published by the Free Software Foundation. 
        
   This program is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY, without even the implied warranty of 
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   General Public License for more details.

   You should have received a copy of the GNU General Public License along 
   with this program, if not, write to the Free Software Foundation, Inc., 
   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

   This program can be provided to you by Xceed Software Inc. under a
   proprietary commercial license agreement for use in non-Open Source
   projects. Visit Xceed.com to find the commercial edition of 
   Xceed Ultimate ListBox for Silverlight.                                    
                                                                      
 **********************************************************************/

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;

namespace Xceed.Silverlight.Data
{
  internal class DataSourceWrapper
  {
    internal DataSourceWrapper( DataSource dataSource )
    {
      if( dataSource == null )
        throw new ArgumentNullException( "dataSource" );

      m_dataSource = dataSource;
    }

    public DataSourceWrapper( DataSourceProvider provider )
      : this( new DataSource( provider ) )
    {
    }

    public event EventHandler<SourceChangedEventArgs> SourceChanged
    {
      add
      {
        if( m_sourceChangedHandler == null )
        {
          m_dataSource.SourceChanged += OnSourceChanged;
        }

        m_sourceChangedHandler += value;
      }
      remove
      {

        m_sourceChangedHandler -= value;

        if( m_sourceChangedHandler == null )
        {
          m_dataSource.SourceChanged -= OnSourceChanged;
        }
      }
    }

    public event EventHandler<ExpansionChangedEventArgs> ExpansionChanged
    {

      add
      {
        if( m_expansionChangedHandler == null )
        {
          m_dataSource.ExpansionChanged += OnExpansionChanged;
        }

        m_expansionChangedHandler += value;
      }
      remove
      {

        m_expansionChangedHandler -= value;

        if( m_expansionChangedHandler == null )
        {
          m_dataSource.ExpansionChanged -= OnExpansionChanged;
        }
      }
    }

    public Type ElementType
    {
      get
      {
        return m_dataSource.ElementType;
      }
    }

    public DataSourceProvider Provider
    {
      get
      {
        return m_dataSource.Provider;

      }
    }

    internal IDataHelper<DataPath> DataHelper
    {
      get
      {
        return m_dataSource.DataHelper;
      }
    }

    internal IEnumerable<SortDescription> PrimaryKeyInfo
    {
      get
      {
        return m_dataSource.PrimaryKeyInfo;
      }
    }

    public Predicate<object> Filter
    {
      get
      {
        return m_dataSource.Filter;
      }
      set
      {
        m_dataSource.Filter = value;
      }
    }

    public FilterExpressionBase FilterExpression
    {
      get
      {
        return m_dataSource.FilterExpression;
      }
      set
      {
        m_dataSource.FilterExpression = value;
      }
    }

    public SortDescription[] AppliedSortDescriptions
    {
      get
      {
        return m_dataSource.AppliedSortDescriptions;
      }
    }

    public SortDescriptionCollection SortDescriptions
    {
      get
      {
        return m_dataSource.SortDescriptions;
      }
    }

    public ObservableCollection<GroupDescription> GroupDescriptions
    {
      get
      {
        return m_dataSource.GroupDescriptions;
      }
    }

    #region IsPrimaryKeyVolatile Property

    internal bool IsPrimaryKeyVolatile
    {
      get
      {
        return m_dataSource.IsPrimaryKeyVolatile;
      }
    }

    #endregion IsPrimaryKeyVolatile Property

    #region GroupHeadersFootersConfigurationProvider

    public IGroupHeadersFootersSelector GroupHeadersFootersSelector
    {
      get
      {
        return m_dataSource.GroupHeadersFootersSelector;
      }

      set
      {
        m_dataSource.GroupHeadersFootersSelector = value;
      }
    }

    #endregion GroupHeadersFootersConfigurationProvider

    #region IGroupStatFunctionsSelector

    public IGroupStatFunctionsSelector GroupStatFunctionsSelector
    {
      get
      {
        return m_dataSource.GroupStatFunctionsSelector;
      }

      set
      {
        m_dataSource.GroupStatFunctionsSelector = value;
      }
    }

    #endregion IGroupStatFunctionsSelector

    public DataPath CreateDataPathForObject( object dataItem )
    {
      if( dataItem == null )
        throw new ArgumentNullException( "dataItem" );

      //DataHelper will create a "full length" DataPath based on the current Grouping of the DataSource.
      return m_dataSource.DataHelper.ConvertItem( dataItem );
    }

    public DataPath CreateDataPathForArray( object[] array )
    {
      if( array == null )
        throw new ArgumentNullException( "array" );

      if( array.Rank != 1 )
        throw new ArgumentException( "The specified array must be unidimensional.", "array" );

      int length = array.Length;

      if( length == 0 )
        throw new ArgumentException( "The specified array cannot be empty.", "array" );

      int groupDescriptionsCount = m_dataSource.GroupDescriptions.Count;

      if( length > ( groupDescriptionsCount + 1 ) )
        throw new ArgumentException( "The specified array contains more elements than the maximum depth of DataPath.", "array" ); 

      DataItem[] dataItems = new DataItem[ length ];

      for( int i = 0; i < length; i++ )
      {
        DataItem dataItem = null;
        if( i < groupDescriptionsCount )
        {
          dataItem = new GroupDataItem( array[ i ] );
        }
        else
        {
          dataItem = m_dataSource.ProviderHelper.Convert( array[ i ] ) as DataItem;
        }
        Debug.Assert( dataItem != null );

        dataItems[ i ] = dataItem;
      }

      return new DataPath( dataItems );
    }

    public object GetDataObjectPropertyValue( string propertyName, DataItem referenceObject )
    {
      return m_dataSource.GetDataObjectPropertyValue( propertyName, referenceObject );
    }

    public DataSourceEnumeratorWrapper GetSyncDataEnumerator()
    {
      return new DataSourceEnumeratorWrapper( m_dataSource.GetSyncDataEnumerator() );
    }

    public void InvalidateData()
    {
      m_dataSource.InvalidateData();
    }

    public void InvalidateDataPath( DataPath dataPath )
    {
      m_dataSource.InvalidateDataPath( dataPath );
    }

    public IEnumerable<FilterOperator> GetSupportedFilterOperators( Type dataType )
    {
      return m_dataSource.GetSupportedFilterOperators( dataType );
    }

    public bool IsGroupPath( DataPath dataPath )
    {
      return m_dataSource.GroupHelper.IsGroupPath( dataPath );
    }

    public bool IsGroupExpanded( int level, DataPath group )
    {
      return !m_dataSource.CollapsedGroupCollections[ level ].Contains( group );
    }

    public bool IsGroupExpanded( DataPath dataPath, out DataPath collapsedGroupPath )
    {
      collapsedGroupPath = null;

      bool isGroupExpanded = true;
      DataPath parentGroupPath = dataPath.ParentPath;

      //If not on the top group.
      if( parentGroupPath != null )
      {
        //Recurse.
        isGroupExpanded = this.IsGroupExpanded( parentGroupPath, out collapsedGroupPath );
      }

      //If no collapsed group found so far.
      if( isGroupExpanded )
      {
        int level = dataPath.Depth - 1;

        //Verify if the current group is collapsed.
        isGroupExpanded = !( dataPath[ level ] is GroupDataItem )
                        || ( this.IsGroupExpanded( level, dataPath ) );

        //If so, keep a reference to the group.
        if( !isGroupExpanded )
        {
          collapsedGroupPath = dataPath;
        }
      }

      return isGroupExpanded;
    }

    public void ExpandGroup( int level, DataPath group )
    {
      m_dataSource.CollapsedGroupCollections[ level ].Remove( group );
    }

    public void CollapseGroup( int level, DataPath group )
    {
      m_dataSource.CollapsedGroupCollections[ level ].Add( group );
    }

    public DataRangeContext GetCurrentDataRangeContext()
    {
      return m_dataSource.GetCurrentDataRangeContext();
    }

    public IDataRangeEnumerator GetDataRangeEnumerator( IEnumerable<DataRange> ranges )
    {
      return m_dataSource.GetDataRangeEnumerator( ranges );
    }

    public IDataRangeEnumerator GetDataRangeEnumerator( 
      IEnumerable<DataRange> ranges,
      IEnumerable<GroupDescription> groupDescriptions,
      IEnumerable<SortDescription> sortDescriptions )
    {
      return m_dataSource.GetDataRangeEnumerator( ranges, groupDescriptions, sortDescriptions );
    }

    private void OnSourceChanged( object sender, SourceChangedEventArgs e )
    {
      m_sourceChangedHandler( this, e );
    }

    private void OnExpansionChanged( object sender, ExpansionChangedEventArgs e )
    {
      m_expansionChangedHandler( this, e );
    }

    private DataSource m_dataSource;//null
    private EventHandler<SourceChangedEventArgs> m_sourceChangedHandler; //null
    private EventHandler<ExpansionChangedEventArgs> m_expansionChangedHandler; //null

    public static object ConvertIfNeeded( object newItemsSource )
    {
      if( newItemsSource is DataSource )
      {
        newItemsSource = new DataSourceWrapper( ( DataSource )newItemsSource );
      }
      return newItemsSource;
    }
  }
}
